{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit Version;

interface

uses Classes, XMLHelper;

type
  TAHMVersionManager = class
  private
    FProductName: String;
    FLatestVersion: String;
    FReleaseDate: String;
    FVersionInfoURL: String;
    FDownloadURL: String;
    FCheckUpdates: Boolean;
    FVersionChecked: Boolean;
    FHelper: TAHMXMLHelper;
  protected
    function BuildURL: String;
    function CompareVersionNumbers(Ver1, Ver2: String): Integer;
    function ParseXML(const VersionData: String): Boolean;
    procedure LogParsingError(const sErrorCode: String);
  public
    property ProductName: String read FProductName;
    property LatestVersion: String read FLatestVersion;
    property ReleaseDate: String read FReleaseDate;
    property VersionInfoURL: String read FVersionInfoURL;
    property DownloadURL: String read FDownloadURL;
    property CheckUpdates: Boolean read FCheckUpdates write FCheckUpdates;
    function CheckNewerVersion: Boolean;
    function PerformUpgrade: Boolean;
  end;

implementation

uses Forms, Controls, SysUtils, UpgradeForm, InternetHelper, ErrorCodes,
     TagNames, Utilities, Logging, AHMTypes;


function TAHMVersionManager.BuildURL: String;
begin
  Result := ApplicationURL + '/checkupdate?product=' + ApplicationName + '&version=' + ApplicationVersion;
end;

function TAHMVersionManager.CheckNewerVersion: Boolean;
begin
  // Set default result - no newer version available
  Result := False;

  // We only check for newer version once and if enabled in preferences
  if FCheckUpdates and (not FVersionChecked) then
  begin
    // Don't check version again
    FVersionChecked := True;

    // Download & parse latest version info file if connection available
    if InternetHelperInstance.IsConnected then
      try
        Result := ParseXML(InternetHelperInstance.Get(BuildURL));
      except
        // Catch any parsing problems and just log them rather than aborting
        on E: Exception do LogWarn('Error checking for updates: ' + E.Message);
      end;

    // If we parsed okay then see if server version is later than ours
    if Result then
      Result := (CompareVersionNumbers(ApplicationVersion, FLatestVersion) > 0);
  end;
end;

function TAHMVersionManager.PerformUpgrade: Boolean;
begin
  // Set default result - upgrade not performed
  Result := False;

  // Prompt user for action to take
  with TfrmUpgradeVersion.Create(Application.MainForm) do
  try
    PopupParent := Application.MainForm;
    if ShowModal = mrOK then Result := True;
  finally
    Free;
  end;
end;

function TAHMVersionManager.CompareVersionNumbers(Ver1, Ver2: String): Integer;
var
  i: Integer;
  S1, S2: TStringList;
begin
  // We use two stringlists to parse delimited version numbers
  S1 := TStringList.Create;
  S2 := TStringList.Create;
  try
    S1.Delimiter := '.';
    S2.Delimiter := '.';
    S1.DelimitedText := Ver1;
    S2.DelimitedText := Ver2;

    // We must ensure both versions have same number of parts
    Result := S2.Count - S1.Count;

    // If same number of parts in each then compare each individually
    for i := 0 to Pred(S1.Count) do
      if Result = 0 then
        Result := StrToIntDef(S2[i], 0) - StrToIntDef(S1[i], 0);
  finally
    S1.Free;
    S2.Free;
  end;
end;

function TAHMVersionManager.ParseXML(const VersionData: String): Boolean;
var
  RootNode, NextNode: TAHMXMLNode;
begin
  // Default result - problem parsing version details
  Result := False;

  // Don't bother trying to parse XML if its empty
  if VersionData = '' then Exit;

  FHelper := TAHMXMLHelper.Create;
  try
    // Parse returned version document response
    FHelper.DocumentXML := VersionData;

    // Ignore preamble - we're looking for a VersionInformation node
    if not FHelper.FindRootElement(RootNode, TAG_VER_INFO) then
      LogParsingError(XML_PRS_VER_MISSING_VER_START_TAG);

    // What nodes do we have here?
    NextNode := nil;
    while FHelper.NextElement(RootNode, NextNode) do
    begin
      if FHelper.CurrentElement = TAG_VER_PROD then
        FProductName := FHelper.GetValue
      else if FHelper.CurrentElement = TAG_VER_NUM then
        FLatestVersion := FHelper.GetValue
      else if FHelper.CurrentElement = TAG_VER_DATE then
        FReleaseDate := FHelper.GetValue
      else if FHelper.CurrentElement = TAG_VER_INFO_URL then
        FVersionInfoURL := FHelper.GetValue
      else if FHelper.CurrentElement = TAG_VER_DOWNLOAD then
        FDownloadURL := FHelper.GetValue
      else
        LogParsingError(XML_PRS_VER_FOUND_UNKNOWN_CONTENT_TAG);
    end;

    // All is well, parsed version information file successfully
    Result := True;
  finally
    FHelper.Free;
  end;
end;

procedure TAHMVersionManager.LogParsingError(const sErrorCode: String);
begin
  // Raise exception for global error handler...
  raise EConfigurationException.Create('Encountered error whilst parsing VPreferences XML: ' + sErrorCode);
end;

end.
